﻿using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace WebAPI_Demo.Filter
{
    /// <summary>
    /// 限流器(利用方法过滤器达到目的)
    /// </summary>
    // 原理：
        //在方法执行前 记录一个时间：请求时间
        //在下一次请求的时候 获取上一次的请求时间 把现在时间 - 上次请求时间 = 间隔时间 <1000ms 就不让请求 拒绝访问
    public class RateLimitActionFilter :Attribute, IActionFilter
    {
        /// <summary>
        /// 内存缓存服务
        /// </summary>
        private readonly IMemoryCache memCache;
        /// <summary>
        /// 限流器类的构造函数 构造注入了 内存缓存服务
        /// </summary>
        /// <param name="memCache"></param>
        public RateLimitActionFilter(IMemoryCache memCache)
        {
            this.memCache = memCache;
        }

        /// <summary>
        /// 执行方法前先执行这
        /// </summary>
        /// <param name="context"></param>
        public void OnActionExecuting(ActionExecutingContext context)
        {
            // 记录请求前

            //获取请求的IP
            string ip = context.HttpContext.Connection.RemoteIpAddress.ToString();
            //组成缓存key key value
            string cacheKey = $"lastvisittick_{ip}";
            //上一次请求的时间 使用IP组成key 获取内存缓存数据 long? 支持空的长整型
            long? lastVisit = memCache.Get<long?>(cacheKey);
            // 如果 上次请求时间为空 或者 现在系统时间 - 上次请求时间 =间隔时间 > 1000ms
            if (lastVisit == null || Environment.TickCount64 - lastVisit > 1000) // Environment.TickCount64 系统时间
            {
                //记录这次请求时间 允许请求
                //            key           value现在系统时间        缓存的过期时间
                memCache.Set(cacheKey, Environment.TickCount64, TimeSpan.FromSeconds(10));//避免长期不访问的用户，占据缓存的内存
            }
            //否则 不允许请求
            else
            {
                //返回一个访问太频繁的 结果
                ObjectResult result = new ObjectResult("访问太频繁")
                {
                    StatusCode = 429
                };
                context.Result = result;
            }
        }

        /// <summary>
        /// 执行方法后执行这
        /// </summary>
        /// <param name="context"></param>
        public void OnActionExecuted(ActionExecutedContext context)
        {
            // 执行方法后
            //请求时间后 - 请求时间前 = 两次请求的间隔
        }

    }
}
